// Copyright 2025 The arm-gic Authors.
// This project is dual-licensed under Apache 2.0 and MIT terms.
// See LICENSE-APACHE and LICENSE-MIT for details.

/// Generates a safe public function named `$function_name` to read the 32-bit system register `$sysreg`.
///
/// This should only be used for system registers which are indeed safe to read.
macro_rules! read_sysreg32 {
    ($sysreg:ident, $opc1:literal, $crm:ident, $crn:ident, $opc2: literal, $function_name:ident) => {
        #[inline]
        #[doc = "Autogenerated function to read the 32-bit "]
        #[doc = stringify!($sysreg)]
        #[doc = " system register"]
        pub fn $function_name() -> u32 {
            let value: u32;
            // SAFETY: The caller of the macro guarantees that this system register is safe to read.
            unsafe {
                core::arch::asm!(
                    concat!(
                        "mrc p15, ",
                        stringify!($opc1), ",",
                        "{value}, ",
                        stringify!($crm), ",",
                        stringify!($crn), ",",
                        stringify!($opc2)
                    ),
                    options(nostack),
                    value = out(reg) value,
                );
            }
            value
        }
    };
}

/// Generates a safe public function named `$function_name` to write to the 32-bit system register
/// `$sysreg`.
///
/// This should only be used for system registers which are indeed safe to write.
macro_rules! write_sysreg32 {
    ($sysreg:ident, $opc1:literal, $crm:ident, $crn:ident, $opc2: literal, $function_name:ident) => {
        #[inline]
        #[doc = "Autogenerated function to write the 32-bit "]
        #[doc = stringify!($sysreg)]
        #[doc = " system register"]
        pub fn $function_name(value: u32) {
            // SAFETY: The caller of the macro guarantees that this system register is safe to
            // write.
            unsafe {
                core::arch::asm!(
                    concat!(
                        "mcr p15, ",
                        stringify!($opc1), ",",
                        "{value}, ",
                        stringify!($crm), ",",
                        stringify!($crn), ",",
                        stringify!($opc2)
                    ),
                    options(nostack),
                    value = in(reg) value,
                );
            }
        }
    };
}

/// Generates a safe public function named `$function_name` to write to the 64-bit system register
/// `$sysreg`.
///
/// This should only be used for system registers which are indeed safe to write.
macro_rules! write_sysreg64 {
    ($sysreg:ident, $opc1:literal, $crm:ident, $function_name:ident) => {
        #[inline]
        #[doc = "Autogenerated function to write the 64-bit "]
        #[doc = stringify!($sysreg)]
        #[doc = " system register"]
        pub fn $function_name(value: u64) {
            // SAFETY: The caller of the macro guarantees that this system register is safe to
            // write.
            let value_lo = value as u32;
            let value_hi = (value >> 32) as u32;
            unsafe {
                core::arch::asm!(
                    concat!(
                        "mcrr p15, ",
                        stringify!($opc1), ",",
                        "{value_lo}, ",
                        "{value_hi}, ",
                        stringify!($crm)
                    ),
                    options(nostack),
                    value_lo = in(reg) value_lo,
                    value_hi = in(reg) value_hi,
                );
            }
        }
    };
}
